#pragma once

#include "pch.h"
#include "MathHelper.h"

inline void d3dSetDebugName(IDXGIObject* obj, const char* name)
{
    if(obj)
    {
        obj->SetPrivateData(WKPDID_D3DDebugObjectName, lstrlenA(name), name);
    }
}
inline void d3dSetDebugName(ID3D12Device* obj, const char* name)
{
    if(obj)
    {
        obj->SetPrivateData(WKPDID_D3DDebugObjectName, lstrlenA(name), name);
    }
}
inline void d3dSetDebugName(ID3D12DeviceChild* obj, const char* name)
{
    if(obj)
    {
        obj->SetPrivateData(WKPDID_D3DDebugObjectName, lstrlenA(name), name);
    }
}

inline std::wstring AnsiToWString(const std::string& str)
{
    WCHAR buffer[512];
    MultiByteToWideChar(CP_ACP, 0, str.c_str(), -1, buffer, 512);
    return std::wstring(buffer);
}

class D3DUtil
{
public:
    static bool IsKeyDown(int InKeyCode);

    static UINT CalcConstantBufferByteSize(UINT InByteSize)
    {
        // 常量缓冲区的大小必须是硬件最小分配空间(通常是256B)的整数倍
        // 为此，要将其凑整为满足需求的最小的256的整数倍。我们现在通过为输入值InByteSize加上255，
        // 再屏蔽求和结果的低2字节(即计算结果中小于256的数据部分)来实现这一点
        // 例如: 假设 InByteSize = 300.
        // (300 + 255) & ~255
        // 555 & ~255
        // 0x022B & ~0x00ff
        // 0x022B & 0xff00
        // 0x0200
        // 512
        return (InByteSize + 255) & ~255;
    }

    static Microsoft::WRL::ComPtr<ID3DBlob> LoadBinary(const std::wstring& InFileName);

    static Microsoft::WRL::ComPtr<ID3DBlob> CompileShader(
        const std::wstring& InFileName,
        const D3D_SHADER_MACRO* InDefines,
        const std::string& InEntryPoint,
        const std::string& InTarget);

    static Microsoft::WRL::ComPtr<ID3D12Resource> CreateDefaultBuffer(
        ID3D12Device* InDevice,
        ID3D12GraphicsCommandList* InCmdList,
        const void* InData,
        UINT64 InByteSize,
        Microsoft::WRL::ComPtr<ID3D12Resource>& OutUploadBuffer);
};

class DxException
{
public:
    DxException() = default;
    DxException(HRESULT hr, const std::wstring& functionName, const std::wstring& filename, int lineNumber);

    std::wstring ToString()const;

    HRESULT ErrorCode = S_OK;
    std::wstring FunctionName;
    std::wstring Filename;
    int LineNumber = -1;
};

#ifndef ThrowIfFailed
#define ThrowIfFailed(x)                                              \
{                                                                     \
HRESULT hr__ = (x);                                               \
std::wstring wfn = AnsiToWString(__FILE__);                       \
if(FAILED(hr__)) { throw DxException(hr__, L#x, wfn, __LINE__); } \
}
#endif

#ifndef ReleaseCom
#define ReleaseCom(x) { if(x){ x->Release(); x = 0; } }
#endif
